package com.gzhryc.jdbc.utils;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.util.*;

/**
 * 对类进行操作的工具方法
 * 
 * @author 张浩
 * @version 1.0.7
 */
public class ClassTools {

	private static List<Class<?>> BaseTypeClasses = new ArrayList<Class<?>>();

	static {
		BaseTypeClasses.add(Integer.class);
		BaseTypeClasses.add(int.class);
		BaseTypeClasses.add(Long.class);
		BaseTypeClasses.add(long.class);
		BaseTypeClasses.add(Double.class);
		BaseTypeClasses.add(double.class);
		BaseTypeClasses.add(Float.class);
		BaseTypeClasses.add(float.class);
		BaseTypeClasses.add(Boolean.class);
		BaseTypeClasses.add(boolean.class);
		BaseTypeClasses.add(String.class);
	}

	/**
	 * 是否是Java基础类型的类
	 * 
	 * @param type
	 * @return
	 */
	public static boolean isBaseType(Class<?> type) {
		for (Class<?> item : BaseTypeClasses) {
			if (type.equals(item)) {
				return true;
			}
		}
		return false;
	}

	public static <T> T copyNotNull(T toObj, Object fromObj) {
		Field[] fields = getDeclaredFields(fromObj.getClass());

		Map<String, Field> targetFields = new HashMap<String, Field>();
		getDeclaredFields(toObj.getClass(), targetFields);

		for (Field field : fields) {
			try {
				field.setAccessible(true);
				if (field.get(fromObj) == null) {
					continue;
				}
				Field target = targetFields.get(field.getName());
				if (target != null && !Modifier.isFinal(target.getModifiers())) {
					target.setAccessible(true);
					if (field.getType().equals(target.getType())) {
						target.set(toObj, field.get(fromObj));
					} else {
						setFieldValue(toObj, target, field.get(fromObj));
					}
				}
			} catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
				e.printStackTrace();
			}
		}
		return toObj;
	}

	/**
	 * 制定属性，当这些属性为空时排除这些属性不进行拷贝
	 * 
	 * @param toObj
	 * @param fromObj
	 * @param strings
	 * @return
	 */
	public static <T> T copyNotNull(T toObj, Object fromObj, String... strings) {
		Field[] fields = getDeclaredFields(fromObj.getClass());

		Map<String, Field> targetFields = new HashMap<String, Field>();
		getDeclaredFields(toObj.getClass(), targetFields);

		for (Field field : fields) {
			try {
				field.setAccessible(true);
				if (field.get(fromObj) == null && ArrayUtils.contains(strings, field.getName())) {
					continue;
				}
				Field target = targetFields.get(field.getName());
				if (target != null && !Modifier.isFinal(target.getModifiers())) {
					target.setAccessible(true);
					if (field.getType().equals(target.getType())) {
						target.set(toObj, field.get(fromObj));
					} else {
						setFieldValue(toObj, target, field.get(fromObj));
					}
				}
			} catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
				e.printStackTrace();
			}
		}
		return toObj;
	}

	@SuppressWarnings("hiding")
	public static <T> T copy(T toObj, Object fromObj) {
		Field[] fields = getDeclaredFields(fromObj.getClass());

		Map<String, Field> targetFields = new HashMap<String, Field>();
		getDeclaredFields(toObj.getClass(), targetFields);

		for (Field field : fields) {
			try {
				field.setAccessible(true);
				Field target = targetFields.get(field.getName());
				if (target != null && !Modifier.isFinal(target.getModifiers())) {
					target.setAccessible(true);
					if (field.getType().equals(target.getType())) {
						target.set(toObj, field.get(fromObj));
					} else {
						setFieldValue(toObj, target, field.get(fromObj));
					}
				}
			} catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
				e.printStackTrace();
			}
		}
		return toObj;
	}

	/**
	 * 拷贝属性
	 * 
	 * @param toObj
	 * @param fromObj
	 * @param strings 排除不拷贝的属性
	 * @return
	 */
	@SuppressWarnings("hiding")
	public static <T> T copy(T toObj, Object fromObj, String... strings) {
		Field[] fields = getDeclaredFields(fromObj.getClass());

		Map<String, Field> targetFields = new HashMap<String, Field>();
		getDeclaredFields(toObj.getClass(), targetFields);

		for (Field field : fields) {
			try {
				field.setAccessible(true);
				if (ArrayUtils.contains(strings, field.getName())) {
					continue;
				}
				Field target = targetFields.get(field.getName());
				if (target != null && !Modifier.isFinal(target.getModifiers())) {
					target.setAccessible(true);
					if (field.getType().equals(target.getType())) {
						target.set(toObj, field.get(fromObj));
					} else {
						setFieldValue(toObj, target, field.get(fromObj));
					}
				}
			} catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
				e.printStackTrace();
			}
		}
		return toObj;
	}

	public static Field[] getDeclaredFields(Class<?> clazz) {
		Field[] fields = clazz.getDeclaredFields();
		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && !Object.class.equals(superclass)) {
			Field[] temp = getDeclaredFields(superclass);
			fields = concat(fields, temp);
		}
		return fields;
	}

	public static Field getDeclaredField(Class<?> clazz, String fieldName) {
		Field field = null;
		try {
			field = clazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException e) {
			Class<?> superclass = clazz.getSuperclass();
			if (superclass != null && !Object.class.equals(superclass)) {
				field = getDeclaredField(superclass, fieldName);
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		return field;
	}

	public static Map<String, Field> getDeclaredFields(Class<?> clazz, Map<String, Field> data) {
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (!data.containsKey(field.getName())) {
				data.put(field.getName(), field);
			}
		}

		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && !Object.class.equals(superclass)) {
			getDeclaredFields(superclass, data);
		}
		return data;
	}

	public static Object getFieldValue(Object obj, String name) {
		Field[] fieldList = getDeclaredFields(obj.getClass());
		for (int i = 0; i < fieldList.length; i++) {
			Field field = fieldList[i];
			if (field.getName().equals(name)) {
				try {
					field.setAccessible(true);
					return field.get(obj);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	@SuppressWarnings("hiding")
	public static <T> boolean setFieldValue(Object obj, Field field, T value) {
		try {
			if (!Modifier.isFinal(field.getModifiers())) {
				if (value != null) {
					field.setAccessible(true);
					field.set(obj, toType(field.getType(), value));
					return true;
				}
			}
		} catch (IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 
	 * @param obj
	 * @param fieldName
	 * @param value
	 * @return
	 */
	@SuppressWarnings("hiding")
	public static <T> boolean setFieldValue(Object obj, String fieldName, T value) {
		try {
			Field field = getDeclaredField(obj.getClass(), fieldName);
			if (field != null && !Modifier.isFinal(field.getModifiers())) {
				field.setAccessible(true);
				field.set(obj, toType(field.getType(), value));
				return true;
			}
		} catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
			e.printStackTrace();
		}
		return false;
	}

	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
		Method method = null;
		try {
			method = clazz.getDeclaredMethod(methodName, parameterTypes);
		} catch (NoSuchMethodException | SecurityException e) {
			// e.printStackTrace();
		}
		if (method == null) {
			Class<?> superclass = clazz.getSuperclass();
			if (superclass != null && !Object.class.equals(superclass)) {
				method = getMethod(superclass, methodName, parameterTypes);
			}
		}
		return method;
	}

	public static Map<String, Method> findDeclaredGetMethods(Class<?> clazz, Map<String, Method> result) {
		if (result == null) {
			result = new HashMap<String, Method>();
		}
		Method[] methods = clazz.getDeclaredMethods();
		if (methods.length > 0) {
			for (Method method : methods) {
				if (method.getName().startsWith("get")) {
					String name = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
					if (!result.containsKey(name)) {
						result.put(name, method);
					}
				}
			}
		}
		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && !Object.class.equals(superclass)) {
			findDeclaredGetMethods(superclass, result);
		}
		return result;
	}

	public static Map<String, Object> findDeclaredGetMethodValues(Object obj, Class<?> clazz, Map<String, Object> result) {
		if (result == null) {
			result = new HashMap<String, Object>();
		}
		Method[] methods = clazz.getDeclaredMethods();
		if (methods.length > 0) {
			for (Method method : methods) {
				if (method.getParameterTypes().length == 0) {
					if (method.getName().length() > 4 && method.getName().startsWith("get")) {
						String name = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
						if (!result.containsKey(name)) {
							try {
								Object value = method.invoke(obj);
								if (value != null) {
									result.put(name, value);
								}
							} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
		Class<?> superclass = clazz.getSuperclass();
		if (superclass != null && !Object.class.equals(superclass)) {
			findDeclaredGetMethodValues(obj, superclass, result);
		}
		return result;
	}

	public static Object getFieldValueByGetMethod(Object obj, String fieldName) {
		String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		Method method = getMethod(obj.getClass(), methodName);
		if (method != null) {
			try {
				return method.invoke(obj);
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	public static boolean setFieldValueBySetMethod(Object obj, String fieldName, Class<?> clazz, Object value) {
		String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
		Method method = getMethod(obj.getClass(), methodName, clazz);
		if (method != null) {
			try {
				method.invoke(obj, toType(clazz, value));
				return true;
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	public static boolean setFieldValueBySetMethod(Object obj, String fieldName, Object value) {
		Field field = getDeclaredField(obj.getClass(), fieldName);
		if (field != null) {
			value = toType(field.getType(), value);
			return setFieldValueBySetMethod(obj, fieldName, field.getType(), value);
		}
		return false;
	}

	@SuppressWarnings({ "unchecked" })
	public static <T> T toType(Class<T> clazz, Object value) {
		if (value == null || StringUtils.isBlank(value.toString())) {
			return null;
		}
		// 转化数据类型
		Object obj = value;
		if (!clazz.equals(value.getClass()) && !clazz.isInstance(value)) {
			if (Integer.class.equals(clazz) || int.class.equals(clazz)) {
				return (T) Integer.valueOf(value.toString().trim());
			} else if (Long.class.equals(clazz) || long.class.equals(clazz)) {
				return (T) Long.valueOf(value.toString().trim());
			} else if (Double.class.equals(clazz) || double.class.equals(clazz)) {
				return (T) Double.valueOf(value.toString().trim());
			} else if (Float.class.equals(clazz) || float.class.equals(clazz)) {
				return (T) Float.valueOf(value.toString().trim());
			} else if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) {
				return (T) Boolean.valueOf(value.toString().trim());
			} else if (Date.class.equals(clazz)) {
				if (String.class.isInstance(value) && StringUtils.isNotBlank(value.toString())) {
					try {
						obj = DateUtils.parseDate(value.toString(), new String[] { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm" });

						return (T) obj;
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			} else if (String.class.equals(clazz)) {
				obj = value.toString().trim(); // 去空格
			}
		}
		try {
			return clazz.cast(obj);
		} catch (ClassCastException e) {
			e.printStackTrace();
		}

		return null;
	}

	@SuppressWarnings("hiding")
	public static <T> T[] concat(T[] first, T[] second) {
		T[] result = Arrays.copyOf(first, first.length + second.length);
		System.arraycopy(second, 0, result, first.length, second.length);
		return result;
	}

	@SuppressWarnings("hiding")
	public static <T> T setFieldValue(T obj, Map<String, Object> data) {
		Field[] fields = getDeclaredFields(obj.getClass());
		for (Field field : fields) {
			Object val = data.get(field.getName());
			if (val != null) {
				setFieldValue(obj, field, val);
			}
		}
		return obj;
	}

	public static Map<String, Object> getFieldValueByGetMethod(Object obj, Class<?> clazz) {
		return findDeclaredGetMethodValues(obj, clazz, null);
	}

	@SuppressWarnings("hiding")
	public static <T> T setValues(T obj, Map<String, String[]> data) {
		Field[] fields = getDeclaredFields(obj.getClass());
		for (Field field : fields) {
			String[] values = data.get(field.getName());
			if (values != null && values.length > 0) {
				if (StringUtils.isNotBlank(values[0])) {
					if (!setFieldValueBySetMethod(obj, field.getName(), field.getType(), values[0])) {
						setFieldValue(obj, field, values[0]);
					}
				}
			}
		}
		return obj;
	}

	@SuppressWarnings("hiding")
	public static <T> T setValues(T obj, Map<String, String[]> data, String... names) {
		Field[] fields = getDeclaredFields(obj.getClass());
		for (Field field : fields) {
			boolean flag = true;
			for (int i = 0; i < names.length; i++) {
				if (names[i].equals(field.getName())) {
					flag = false;
					break;
				}
			}
			if (flag) {
				String[] values = data.get(field.getName());
				if (values != null) {
					if (values.length == 1) {
						if (StringUtils.isNotBlank(values[0])) {
							if (!setFieldValueBySetMethod(obj, field.getName(), field.getType(), values[0])) {
								setFieldValue(obj, field, values[0]);
							}
						}
					} else {

					}
				}
			}
		}
		return obj;
	}

	/**
	 * 查询两个对象值不一致的属性
	 * 
	 * @param obj1
	 * @param obj2
	 * @return
	 */
	public static <T> List<String> findChangeField(T obj1, T obj2) {
		List<String> result = new ArrayList<String>();
		Class<?> clazz = obj1.getClass();
		Field[] fields = ClassTools.getDeclaredFields(clazz);
		if (fields != null && fields.length > 0) {
			for (Field field : fields) {
				try {
					if (ClassTools.isBaseType(field.getType())) {
						field.setAccessible(true);
						Object val1 = field.get(obj1);
						Object val2 = field.get(obj2);
						if (val1 != null && val2 != null) {
							if (!val1.equals(val2)) {
								result.add(field.getName());
							}
						} else if (!(val1 == null && val2 == null)) {
							result.add(field.getName());
						}
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}

	/**
	 * 将实体类转换成Map对象
	 * 
	 * @param obj
	 * @return
	 */
	public static Map<String, Object> toMap(Object obj) {
		if (obj != null) {
			Map<String, Object> map = new HashMap<>();
			Field[] fields = getDeclaredFields(obj.getClass());
			if (fields != null && fields.length > 0) {
				for (int i = 0; i < fields.length; i++) {
					try {
						Field field = fields[i];
						boolean flag = field.isAccessible();
						field.setAccessible(true);
						map.put(field.getName(), field.get(obj));
						field.setAccessible(flag);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			return map;
		}
		return null;
	}
}
